<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>通话</title>
    <script src="js/vue.js"></script>
    <script type="text/javascript" src="js/uni.webview.1.5.5.js"></script>
</head>
<body>
<div id="app">
    <video ref="videoRef" :class="[!isRemote?'mainVideo':'remoteVideo']" class=""></video>
    <video ref="remoteVideoRef" :class="[isRemote?'mainVideo':'remoteVideo']" v-show="isRemote"></video>
    <button @click="getLocalStream()" style="width: 200px;height: 100px">按钮</button>
    <div class="infoBox">
        <div class="name">
            {{name}}
        </div>
        <div class="title">
            {{mainStatus == '0' ? '正在呼叫' : mainStatus == '1' ? '来电' : mainStatus == '2' ? '通话中...' : ''}}
        </div>
    </div>
    <div class="button">
        <div class="btn" v-if="['0','1','2'].indexOf(mainStatus)!= -1">
            挂断
        </div>
        <div class="btn" style="margin-left: 5rem;background-color:#00F33E" @click="acceptCall"
             v-if="['1'].indexOf(mainStatus)!= -1">
            接听
        </div>
    </div>

    <div class="button" style="bottom: 10px">
        <div style="padding: 15px 30px;background-color: white" @click="callRoom">
            发起呼叫
        </div>
    </div>
</div>
</body>
<script>
    new Vue({
        el: '#app',
        data() {
            return {
                isRemote:false,
                id: '',
                name: '',
                mainStatus: '0',//0 呼叫中(显示来电信息、挂断按钮) 1 来电中(显示来电信息、接通\挂断按钮) 2 通话中(显示来电信息、挂断按钮)
                peer: '',
                localStream: '',
                offer:'',
                answer:'',
                cndidates:[],
            }
        },
        async mounted() {
            window.addEventListener('message', (e) => {
                let data = e.data
                this.handleUniappMsg(data)
            }, false);
            window.getUniAppMessage = (arg) => {
                let data = JSON.parse(arg)
                this.handleUniappMsg(data)
            }
            let params = this.getUrlParams(window.location.href)
            this.name = params.name
            this.id = params.id
            this.mainStatus = params.status
            this.localStream = await this.getLocalStream()
            this.$refs.videoRef.srcObject = this.localStream
            this.$refs.videoRef.play()
        },
        methods: {
            //处理Uniapp发送过来的消息
            async handleUniappMsg(data) {
                switch (data.action) {
                    //接收offer
                    case 'repSendOffer':
                        //接收字符串格式offer
                        let offerData = JSON.parse(atob(data.data))
                        //还原offer数据类型
                        this.offer = new RTCSessionDescription(offerData);
                        //接收方创建本地视频流
                        let stream = await this.getLocalStream()
                        const configuration = {
                            iceServers: [
                                // STUN服务器配置
                                {
                                    urls: ['stun:114.115.142.111:9998'],
                                    username: 'yangye',
                                    credential: '230147961',
                                },
                                // TURN服务器配置（假设同时支持UDP和TCP）
                                {
                                    urls: [
                                        'turn:114.115.142.111:9998?transport=udp', // UDP传输
                                        'turn:114.115.142.111:9998?transport=tcp', // TCP传输
                                    ],
                                    username: 'yangye',
                                    credential: '230147961',
                                },
                            ],
                        };
                        this.peer = new RTCPeerConnection(configuration)
                        this.peer.addStream(stream)
                        //监听candidate
                        this.peer.onicecandidate = (e)=>{
                            if(e.candidate){
                                uni.postMessage({
                                    data: {
                                        action:'sendCandidate',
                                        data: {
                                            id: this.id,
                                            name: this.name,
                                            candidate: JSON.stringify(e.candidate)
                                        },
                                    }
                                });
                            }
                        }
                        this.peer.onaddstream = (e)=>{
                            this.isRemote = true
                            this.$refs.remoteVideoRef.srcObject = e.stream
                            this.$refs.remoteVideoRef.play()
                        }
                        //接收方设置本地offer
                        await this.peer.setRemoteDescription(this.offer)
                        console.log('接收方接听 接收方offer',this.offer)
                        //接收方创建answer
                        this.answer = await this.peer.createAnswer()
                        // 设置本地answer
                        await this.peer.setLocalDescription(this.answer)
                        console.log('接收方回应 接收方answer',this.answer)
                        this.mainStatus='2'
                        uni.postMessage({
                            data: {
                                action: 'sendAnswer',
                                data: {
                                    id: this.id,
                                    name: this.name,
                                    answer: JSON.stringify(this.answer)
                                },
                            }
                        });
                        //接收完answer添加cndidates
                        for(let candidate of this.cndidates){
                            console.log('candidate====',candidate)
                            await this.peer.addIceCandidate(candidate)
                        }
                        break;
                    // 接收answer
                    case 'repSendAnswer':
                        //接收字符串格式answer
                        let answerData = JSON.parse(atob(data.data))
                        //还原answer数据类型
                        this.answer = new RTCSessionDescription(answerData);
                        await this.peer.setRemoteDescription(this.answer)
                        this.mainStatus='2'
                        //接收完answer添加cndidates
                        for(let candidate of this.cndidates){
                            await this.peer.addIceCandidate(candidate)
                        }
                        break;
                    // 接收Candidate
                    case 'repSendCandidate':
                        //接收字符串格式answer
                        let candidateData = JSON.parse(atob(data.data))
                        const cndidate = new RTCIceCandidate(candidateData);
                        this.cndidates.push(cndidate)
                        console.log('Candidate',cndidate)
                        break;
                }
            },
            //接听电话
            acceptCall() {
                //兼容APP事件
                uni.postMessage({
                    data: {
                        action: 'acceptCall',
                        data: {
                            id: this.id,
                            name: this.name,
                        },
                    }
                });
            },
            //测试呼叫事件
            async callRoom() {
                const configuration = {
                    iceServers: [
                        // STUN服务器配置
                        {
                            urls: ['stun:114.115.142.111:9998'],
                            username: 'yangye',
                            credential: '230147961',
                        },
                        // TURN服务器配置（假设同时支持UDP和TCP）
                        {
                            urls: [
                                'turn:114.115.142.111:9998?transport=udp', // UDP传输
                                'turn:114.115.142.111:9998?transport=tcp', // TCP传输
                            ],
                            username: 'yangye',
                            credential: '230147961',
                        },
                    ],
                };
                this.peer = new RTCPeerConnection(configuration)
                this.peer.addStream(this.localStream)
                // 监听呼叫方candidate
                this.peer.onicecandidate = (e)=>{
                    if(e.candidate){
                        uni.postMessage({
                            data: {
                                action:'sendCandidate',
                                data: {
                                    id: this.id,
                                    name: this.name,
                                    candidate: JSON.stringify(e.candidate)
                                },
                            }
                        });
                    }
                }
                this.peer.onaddstream = (e)=>{
                    this.isRemote = true
                    this.$refs.remoteVideoRef.srcObject = e.stream
                    this.$refs.remoteVideoRef.play()
                }

                // 创建offer
                this.offer = await this.peer.createOffer({
                    offerToReceiveAudio: 1,
                    offerToReceiveVideo: 1
                })
                //设置本地offer
                await this.peer.setLocalDescription(this.offer)
                this.mainStatus = '0'
                console.log('发起呼叫 呼叫方offer',this.offer)
                //兼容APP事件
                uni.postMessage({
                    data: {
                        action: 'call',
                        data: {
                            id: this.id,
                            name: this.name,
                            offer: JSON.stringify(this.offer)
                        },
                    }
                });
            },
            getLocalStream() {
                return new Promise((seccess, error) => {
                    this.getUserMedia({
                        audio: false,
                        video: {
                            width: {
                                ideal: 430
                            },
                            height: {
                                ideal: 720
                            },
                            frameRate: {
                                ideal: 30
                            }
                        }
                    }, (stream) => {

                        seccess(stream)

                    }, (err) => {

                        error(err)
                    })
                })
            },
            //验证API是否存在
            getUserMedia(constraints, success, error) {
                if (navigator.mediaDevices.getUserMedia) {
                    //最新的标准API
                    navigator.mediaDevices
                        .getUserMedia(constraints)
                        .then(success)
                        .catch(error);
                } else if (navigator.webkitGetUserMedia) {
                    //webkit核心浏览器
                    navigator.webkitGetUserMedia(constraints, success, error);
                } else if (navigator.mozGetUserMedia) {
                    //firfox浏览器
                    navigator.mozGetUserMedia(constraints, success, error);
                } else if (navigator.getUserMedia) {
                    //旧版API
                    navigator.getUserMedia(constraints, success, error);
                }
            },
            getUrlParams(url) {
                var params = {};
                var parser = document.createElement('a');
                parser.href = url;

                var queryStr = parser.search.substring(1);
                var paramsArr = queryStr.split('&');

                for (var i = 0; i < paramsArr.length; i++) {
                    var param = paramsArr[i].split('=');
                    params[param[0]] = decodeURIComponent(param[1]);
                }

                return params;
            }

        }
    })
</script>
<style>
    .mainVideo {
        position: fixed;
        top: 0;
        left: 0;
        background-color: #333;
        width: 100vw;
        height: 100vh;
        object-fit: cover;
        z-index: 9;
        transition: 0.6s;
    }
    .remoteVideo{
        position: absolute;
        bottom: 15px;
        right: 15px;
        background-color: #333;
        width: 150px;
        height: 220px;
        border-radius: 15px;
        overflow: hidden;
        z-index: 10;
        object-fit: cover;
        transition: 0.6s;
    }

    .infoBox {
        position: absolute;
        top: 200px;
        left: 0;
        width: 100%;
        z-index: 99;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
    }

    .name {
        font-size: 4rem;
        font-weight: bold;
        color: #fff;
        text-shadow: 2px 2px 15px #333;
    }

    .title {
        font-size: 2rem;
        color: #fff;
    }

    .button {
        position: absolute;
        bottom: 10rem;
        left: 0;
        width: 100%;
        z-index: 99;
        display: flex;
        justify-content: center;
    }

    .btn {
        box-shadow: 2px 2px 5px 10px rgba(0, 0, 0, 0.1);
        background-color: red;
        color: #fff;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 20px;
        font-weight: bold;
        width: 5rem;
        height: 5rem;
        border-radius: 50%;
        transition: 0.1s;
    }

    .btn {
        transform: scale(1.1);
    }

    .btn:active {
        transform: scale(0.9);
    }
</style>
</html>